/*
 * atcmd.c
 *
 *  Created on: Dec 25, 2021
 *      Author: soliber
 */
//Include
#include "wifi.h"


//Typedef


//Define
#define LOG_TAG "atcmd"

#define AT_TIMEOUT 100
#define AT_REPLY_MAX_LEN 64

#define EVT_TXCPLT	(1<<0)
#define EVT_RXCPLT	(1<<1)

#define AT_OK_STRING "+OK"
#define AT_ERROR_STRING "+ERR"
#define AT_START_STRING "e103-w08 start"
#define AT_ENTER_STRING	"enter AT mode"
#define AT_EXIT_STRING	"exit AT mode"
#define AT_NOLINK_STRING "no link"
#define AT_TCP_SINGLE_STRING "tcp single"
#define AT_TCP_MULTY_STRING "tcp multy"
#define AT_HEADER_ERROR_STRING "header error"
#define SYNC_HEADER "\AA\FE\55"


//Macro


//Variables
static osEventFlagsId_t evt;
static char reply[AT_REPLY_MAX_LEN];


//Function Prototypes
static int16_t AT_Send(uint8_t *buf, uint16_t len, uint32_t timeout);
static int16_t AT_Receive(uint8_t *buf, uint16_t len, uint32_t timeout);
static void AT_TxCpltCallback(UART_HandleTypeDef *huart);
static void AT_RxCpltCallback(UART_HandleTypeDef *huart);

/**
 * @brief 向模块发送数据
 *
 * @param buf 发送缓冲区
 * @param len 发送数据长度
 * @param timeout 超时时间（ms）
 *
 * @return 操作状态
 * @retval >=0 已发送数据长度
 * @retval -1 DMA错误
 * @retval -2 DMA正忙
 */
static int16_t AT_Send(uint8_t *buf, uint16_t len, uint32_t timeout)
{
	uint32_t ret;
	uint32_t tick = osKernelGetSysTimerCount();
	uint16_t sent_len;

	///清事件
	osEventFlagsClear(evt, EVT_TXCPLT);

	if(len==0)	return 0;

	do
	{
		///启动DMA发送
		ret = HAL_UART_Transmit_DMA(&wifi_uart, buf, len);
		if(ret == HAL_OK)
		{
			timeout -= osKernelGetSysTimerCount()-tick;

			///开始发送，等待发送完成事件
			ret = osEventFlagsWait(evt, EVT_TXCPLT, osFlagsWaitAll, timeout);

			///计算已发送数据量
			sent_len = len - wifi_uart.hdmatx->Instance->CNDTR;

			///发送完成事件错误
			if(ret & osFlagsError)
			{
				///终止传输
				HAL_UART_AbortTransmit(&wifi_uart);
				if(ret == osFlagsErrorTimeout)
				{
					///超时
					log_e("evt txcplt timeout");
				}
				else
				{
					///其他错误
					log_e("evt txcplt error:%d",ret);
				}
			}
			///已发送数据量
			return sent_len;
		}
		else if(ret == HAL_BUSY)
		{
			///忙等待
			osDelay(1);
		}
		else
		{
			///发送出错
			HAL_UART_AbortTransmit(&wifi_uart);
			log_e("tx error:%d",ret);
			return -1;
		}
	}while(osKernelGetSysTimerCount() - tick < timeout);

	///DMA正忙
	return -2;
}

/**
 * @brief 从模块接收数据
 *
 * @param buf 接收缓冲区
 * @param len 接收缓冲区长度
 * @param timeout 超时时间（ms）
 *
 * @return 操作状态
 * @retval >=0 已接收数据长度
 * @retval -1 DMA错误
 * @retval -2 DMA正忙
 */
static int16_t AT_Receive(uint8_t *buf, uint16_t len, uint32_t timeout)
{
	uint32_t ret;
	uint32_t tick = osKernelGetSysTimerCount();
	uint16_t recv_len;

	///清事件
	osEventFlagsClear(evt, EVT_RXCPLT);

	do
	{
		///启动DMA接收
		ret = HAL_UART_Receive_DMA(&wifi_uart, buf, len);
		if(ret == HAL_OK)
		{
			timeout -= osKernelGetSysTimerCount()-tick;

			///开始接收，等待接收完成事件
			ret = osEventFlagsWait(evt, EVT_RXCPLT, osFlagsWaitAll, timeout);

			///计算已接收数据量
			recv_len = len - wifi_uart.hdmarx->Instance->CNDTR;

			///接收完成事件错误
			if(ret & osFlagsError)
			{
				///停止DMA传输
				HAL_UART_AbortReceive(&wifi_uart);
				if(ret == osFlagsErrorTimeout)
				{
					///超时
					log_d("evt rxcplt timeout");
				}
				else
				{
					///其他错误
					log_e("evt rxcplt error:%d",ret);
				}
			}
			///已接收数据量
			return recv_len;
		}
		else if(ret == HAL_BUSY)
		{
			///忙等待
			osDelay(1);
		}
		else
		{
			///接收出错
			HAL_UART_AbortReceive_IT(&wifi_uart);
			log_e("rx error:%d",ret);
			return -1;
		}
	}while(osKernelGetSysTimerCount() - tick < timeout);

	///DMA正忙
	return -2;
}

/**
 * @brief 发送完成回调
 *
 * @param huart 串口HAL句柄
 */
static void AT_TxCpltCallback(UART_HandleTypeDef *huart)
{
	assert(huart == &wifi_uart);
	osEventFlagsSet(evt, EVT_TXCPLT);
}

/**
 * @brief 接收完成回调
 *
 * @param huart 串口HAL句柄
 */
static void AT_RxCpltCallback(UART_HandleTypeDef *huart)
{
	assert(huart == &wifi_uart);
	osEventFlagsSet(evt, EVT_RXCPLT);
}

/**
 * @brief 执行AT指令，响应保存在静态buffer @ref reply 中
 *
 * @param cmd 要发送的指令
 * @return 操作状态
 * @retval 0 指令执行成功
 * @retval -1 指令发送成功，接收错误
 * @retval -2 指令发送失败
 */
int8_t AT_Execute(char* cmd)
{
	int16_t sent_len = strlen((char*)cmd);
	int16_t reply_len = 0;

	*reply = '\0';

	///发送指令
	if(AT_Send((uint8_t*)cmd, sent_len, AT_TIMEOUT) == sent_len)
	{
		///立即接收响应
		reply_len = AT_Receive((uint8_t*)reply, AT_REPLY_MAX_LEN, AT_TIMEOUT);

		///接收到一些数据
		if(reply_len>0)
		{
			///结束符
			*(reply + min(reply_len, AT_REPLY_MAX_LEN)) = '\0';

			///回显
			log_d("\r\nAT CMD:\r\n%s\r\nAT RET:\r\n%s\r\n",cmd,reply);

			return 0;
		}
		return -1;
	}
	return -2;
}

/**
 * @brief 进入AT模式
 *
 * @return 操作状态
 * @retval 0 成功
 * @retval -1 失败
 */
int8_t AT_Enter(void)
{
	//暂停解析任务
	ParserDeInit();

	///创建事件
	evt = osEventFlagsNew(NULL);
	assert(evt != NULL);

	//设置回调
	wifi_uart.RxCpltCallback = AT_RxCpltCallback;
	wifi_uart.TxCpltCallback = AT_TxCpltCallback;

	//发送指令
	if(AT_Execute("+++")==0)
	{
		if(strstr(reply,"enter AT mode")!=NULL)
		{
			return 0;
		}
	}
	return -1;
}

/**
 * @brief 退出AT模式
 *
 * @return 操作状态
 * @retval 0 成功
 * @retval -1 失败
 */
int8_t AT_Exit(void)
{
	if(AT_Execute("AT+EXIT\r\n")==0)
	{
		if(strstr(reply,"exit AT mode")!=NULL)
		{
			///删除事件
			osEventFlagsDelete(evt);

			//继续
			ParserInit();
			return 0;
		}
	}
	return -1;
}

/**
 * @brief 解析AT+GMR的响应消息，如果解析成功，结果将放到参数中
 *
 * @param version 解析结果要保存的地址
 * @return 解析结果
 * @retval 0 解析成功
 * @retval -1 解析失败
 */
int8_t AT_Parser_GMR(uint16_t *version)
{
	assert(version != NULL);

	int p[4];
	uint8_t n;

	n = sscanf(reply,"+GMR:SW:V%d.%d,HD:V%d.%d",&p[3],&p[2],&p[1],&p[0]);
	if(n == 4)
	{
		*version = 0;
		for(uint8_t i = 0;i<4;i++)
		{
			*version += (uint16_t)p[i]<<(4*i);
		}
		return 0;
	}
	return -1;
}

/**
 * @brief 解析响应是否OK，如果不OK则返回错误码
 *
 * @return 解析结果
 * @retval 0 解析成功，响应OK
 * @retval -1~-30 解析出错误码，参考 E103-W08A Datasheet 第7.1节
 * @retval -32 解析错误
 */
int8_t AT_Parser_OK(void)
{
	if(strstr(reply,"+OK"))
	{
		return 0;
	}

	if(strstr(reply,"+ERR"))
	{
		short err;
		if(sscanf(reply,"+ERR=%hd",&err))
		{
			return err;
		}
	}
	return -32;
}

/**
 * @brief 解析AT+CWMAC的响应消息，如果解析成功，结果将放到参数中
 *
 * @param mac 解析结果要保存的地址
 * @return 解析结果
 * @retval 0 解析成功
 * @retval -1 解析失败
 */
int8_t AT_Parser_CWMAC(uint64_t *mac)
{
	assert(mac != NULL);

	int p[6];
	uint8_t n;

	n = sscanf(reply,"+OK=%x:%x:%x:%x:%x:%x",&p[5],&p[4],&p[3],&p[2],&p[1],&p[0]);
	if(n == 6)
	{
		*mac = 0;
		for(uint8_t i=0;i<6;i++)
		{
			*mac += (uint64_t)p[i]<<(8*i);
		}
		return 0;
	}
	return -1;
}

/**
 * @brief 解析AT+CIFSR的响应消息，如果解析成功，结果将放到参数中
 *
 * @param ip 解析结果要保存的地址
 * @return 解析结果
 * @retval 0 解析成功
 * @retval -1 解析失败
 */
int8_t AT_Parser_CIFSR(uint32_t *ip)
{
	assert(ip != NULL);

	int p[4];
	uint8_t n;

	n = sscanf(reply,"+OK=%d.%d.%d.%d",&p[3],&p[2],&p[1],&p[0]);
	if(n == 4)
	{
		*ip = 0;
		for(uint8_t i = 0;i<4;i++)
		{
			*ip += (uint32_t)p[i]<<(8*i);
		}
		return 0;
	}
	return -1;
}

/**
 * @brief 解析AT+CIPSTATUS的响应消息，如果解析成功，结果将放到参数中
 * @note 目前只支持多连接！
 * @param link 连接状态
 * @return 解析结果
 * @retval 0 解析成功
 * @retval -1 解析失败
 */
int8_t AT_Parser_CIPSTATUS(int8_t link[4])
{
	assert(link != NULL);

	int p[4];
	uint8_t n;

	n = sscanf(reply,"+OK=%d,%d,%d,%d",&p[0],&p[1],&p[2],&p[3]);
	if(n == 4)
	{
		for(uint8_t i=0;i<4;i++)
		{
			link[i] = p[i];
		}
		return 0;
	}
	return -1;
}

/**
 * @brief 解析AT+BLENAME的响应消息，如果解析成功，结果将放到参数中
 *
 * @param name 存放名字的buffer，不大于30字节
 *
 * @return 操作状态
 * @retval 0 解析成功
 * @retval -1 解析失败
 */
int8_t AT_Parser_BLENAME(char* name)
{
	assert(name != NULL);

	uint8_t n;
	n = sscanf(reply,"+0K=%s",name);//模块BUG：这里是0K而不是OK

	return (n==1)?0:-1;
}
